package com.fs.files.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;

public class FileUtils {
	
	private static final Logger logger = LoggerFactory.getLogger(FileUtils.class);

	/**
	 * 文件或目录是否存在
	 */
	public static boolean exists(String path) {
		return new File(path).exists();
	}

	/**
	 * 文件是否存在
	 */
	public static boolean existsFile(String path) {
		File file = new File(path);
		return file.exists() && file.isFile();
	}

	/**
	 * 文件或目录是否存在
	 */
	public static boolean existsAny(String... paths) {
		return Arrays.stream(paths).anyMatch(path -> new File(path).exists());
	}

	/**
	 * 删除文件或文件夹
	 */
	public static void deleteIfExists(File file) throws IOException {
		if (file.exists()) {
			if (file.isFile()) {
				if (!file.delete()) {
					throw new IOException("Delete file failure,path:" + file.getAbsolutePath());
				}
			} else {
				File[] files = file.listFiles();
				if (files != null && files.length > 0) {
					for (File temp : files) {
						deleteIfExists(temp);
					}
				}
				if (!file.delete()) {
					throw new IOException("Delete file failure,path:" + file.getAbsolutePath());
				}
			}
		}
	}

	/**
	 * 删除文件或文件夹
	 */
	public static void deleteIfExists(String path) throws IOException {
		deleteIfExists(new File(path));
	}

	/**
	 * 创建文件，如果目标存在则删除
	 */
	public static File createFile(String path) throws IOException {
		return createFile(path, false);
	}

	/**
	 * 创建文件夹，如果目标存在则删除
	 */
	public static File createDir(String path) throws IOException {
		return createDir(path, false);
	}

	/**
	 * 创建文件，如果目标存在则删除
	 */
	public static File createFile(String path, boolean isHidden) throws IOException {
		File file = createFileSmart(path);
		// if (OsUtil.isWindows()) {
		Files.setAttribute(file.toPath(), "dos:hidden", isHidden);
		// }
		return file;
	}

	/**
	 * 创建文件夹，如果目标存在则删除
	 */
	public static File createDir(String path, boolean isHidden) throws IOException {
		File file = new File(path);
		deleteIfExists(file);
		File newFile = new File(path);
		newFile.mkdir();
		// if (OsUtil.isWindows()) {
		Files.setAttribute(newFile.toPath(), "dos:hidden", isHidden);
		// }
		return file;
	}

	/**
	 * 查看文件或者文件夹大小
	 */
	public static long getFileSize(String path) {
		File file = new File(path);
		if (file.exists()) {
			if (file.isFile()) {
				return file.length();
			} else {
				long size = 0;
				File[] files = file.listFiles();
				if (files != null && files.length > 0) {
					for (File temp : files) {
						if (temp.isFile()) {
							size += temp.length();
						}
					}
				}
				return size;
			}
		}
		return 0;
	}

	public static File createFileSmart(String path) throws IOException {
		try {
			File file = new File(path);
			if (file.exists()) {
				file.delete();
				file.createNewFile();
			} else {
				createDirSmart(file.getParent());
				file.createNewFile();
			}
			return file;
		} catch (IOException e) {
			throw new IOException("createFileSmart=" + path, e);
		}
	}

	public static File createDirSmart(String path) throws IOException {
		try {
			File file = new File(path);
			if (!file.exists()) {
				Stack<File> stack = new Stack<>();
				File temp = new File(path);
				while (temp != null) {
					stack.push(temp);
					temp = temp.getParentFile();
				}
				while (stack.size() > 0) {
					File dir = stack.pop();
					if (!dir.exists()) {
						dir.mkdir();
					}
				}
			}
			return file;
		} catch (Exception e) {
			throw new IOException("createDirSmart=" + path, e);
		}
	}

	/**
	 * 获取目录所属磁盘剩余容量
	 */
	public static long getDiskFreeSize(String path) {
		File file = new File(path);
		return file.getFreeSpace();
	}

	/**
	 * 去掉后缀名
	 */
	public static String getFileNameNoSuffix(String fileName) {
		int index = fileName.lastIndexOf(".");
		if (index != -1) {
			return fileName.substring(0, index);
		}
		return fileName;
	}

	public static void initFile(String path, boolean isHidden) throws IOException {
		initFile(path, null, isHidden);
	}

	public static void initFile(String path, InputStream input, boolean isHidden) throws IOException {
		if (exists(path)) {
			try (RandomAccessFile raf = new RandomAccessFile(path, "rws")) {
				raf.setLength(0);
			}
		} else {
			FileUtils.createFile(path, isHidden);
		}
		if (input != null) {
			try (RandomAccessFile raf = new RandomAccessFile(path, "rws")) {
				byte[] bts = new byte[8192];
				int len;
				while ((len = input.read(bts)) != -1) {
					raf.write(bts, 0, len);
				}
			} finally {
				input.close();
			}
		}
	}

	public static void createCurDir(String path){
		File file = new File(path);
		if (!file.exists()) {
			file.mkdir();
		}
	}

	public static String createFileBySysTime(String path) {

		// 1. 读取系统时间
		Calendar calendar = Calendar.getInstance();
		Date time = calendar.getTime();

		// 2. 格式化系统时间
		SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
		String fileName = "t"+format.format(time); //获取系统当前时间并将其转换为string类型,fileName即文件名

		// 3. 创建文件夹
		String newPath = path +"\\"+ fileName;
		File file = new File(newPath);
		//如果文件目录不存在则创建目录
		if (!file.exists()) {
			if (!file.mkdir()) {
				System.out.println("当前路径不存在，创建失败");
				return null;
			}
		}
		System.out.println("创建成功" + newPath);
		return fileName;
	}

	public static List<String> getfiles(String path) {
		List<String> list = new ArrayList<String>();
		File f = new File(path);
		File fa[] = f.listFiles();
		for (int i = 0; i < fa.length; i++) {
			File fs = fa[i];
			if (fs.isDirectory()) {
				System.out.println(fs.getName() + " [目录]");
			} else {
				list.add(fs.getName());
			}
		}
		return list;
	}

	public static boolean canWrite(String path) {
		File file = new File(path);
		File test;
		if (file.isFile()) {
			test = new File(file.getParent() + File.separator + UUID.randomUUID().toString() + ".test");
		} else {
			test = new File(file.getPath() + File.separator + UUID.randomUUID().toString() + ".test");
		}
		try {
			test.createNewFile();
			test.delete();
		} catch (IOException e) {
			return false;
		}
		return true;
	}

	/**
	 *
	 * zip解压
	 *
	 * @param srcFile
	 *            zip源文件
	 *
	 * @param destDirPath
	 *            解压后的目标文件夹
	 *
	 * @throws RuntimeException
	 *             解压失败会抛出运行时异常
	 *
	 */

	public static void unZip(File srcFile, String destDirPath) throws RuntimeException {

		// 判断源文件是否存在
		if (!srcFile.exists()) {
			throw new RuntimeException(srcFile.getPath() + "所指文件不存在");
		}
		// 开始解压
		ZipFile zipFile = null;
		try {
			zipFile = new ZipFile(srcFile, Charset.forName("GBK"));
			Enumeration<?> entries = zipFile.entries();
			while (entries.hasMoreElements()) {
				ZipEntry entry = (ZipEntry) entries.nextElement();

				// 如果是文件夹，就创建个文件夹
				if (entry.isDirectory()) {
					String dirPath = destDirPath + "/" + entry.getName();
					File dir = new File(dirPath);
					dir.mkdirs();
				} else {
					// 如果是文件，就先创建一个文件，然后用io流把内容copy过去
					File targetFile = new File(destDirPath + "/" + entry.getName());
					// 保证这个文件的父文件夹必须要存在
					if (!targetFile.getParentFile().exists()) {
						targetFile.getParentFile().mkdirs();
					}
					targetFile.createNewFile();
					// 将压缩文件内容写入到这个文件中
					InputStream is = zipFile.getInputStream(entry);
					FileOutputStream fos = new FileOutputStream(targetFile);
					int len;
					byte[] buf = new byte[(int) entry.getSize()];
					while ((len = is.read(buf)) != -1) {
						fos.write(buf, 0, len);
					}
					// 关流顺序，先打开的后关闭
					fos.close();
					is.close();
				}
			}
		} catch (Exception e) {
			throw new RuntimeException("unzip error from ZipUtils", e);
		} finally {
			if (zipFile != null) {
				try {
					zipFile.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public static void unzipold(String zipPath, String toPath, String... unzipFile) throws IOException {
		try (ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(zipPath))) {
			toPath = toPath == null ? new File(zipPath).getParent() : toPath;
			ZipEntry entry;
			while ((entry = zipInputStream.getNextEntry()) != null) {
				final String entryName = entry.getName();
				if (entry.isDirectory() || (unzipFile != null && unzipFile.length > 0
						&& Arrays.stream(unzipFile).noneMatch((file) -> entryName.equalsIgnoreCase(file)))) {
					zipInputStream.closeEntry();
					continue;
				}
				File file = createFileSmart(toPath + File.separator + entryName);
				try (FileOutputStream outputStream = new FileOutputStream(file)) {
					byte[] bts = new byte[8192];
					int len;
					while ((len = zipInputStream.read(bts)) != -1) {
						outputStream.write(bts, 0, len);
					}
				}
			}
		}
	}

	/**
	 * 判断文件存在是重命名
	 */
	public static String renameIfExists(String path) {
		File file = new File(path);
		if (file.exists() && file.isFile()) {
			int index = file.getName().lastIndexOf(".");
			String name = file.getName().substring(0, index);
			String suffix = index == -1 ? "" : file.getName().substring(index);
			int i = 1;
			String newName;
			do {
				newName = name + "(" + i + ")" + suffix;
				i++;
			} while (existsFile(file.getParent() + File.separator + newName));
			return newName;
		}
		return file.getName();
	}

	/**
	 * 创建指定大小的Sparse File
	 */
	public static void createFileWithSparse(String filePath, long length) throws IOException {
		Path path = Paths.get(filePath);
		try {
			Files.deleteIfExists(path);
			try (SeekableByteChannel channel = Files.newByteChannel(path, StandardOpenOption.CREATE_NEW,
					StandardOpenOption.WRITE, StandardOpenOption.SPARSE)) {
				channel.position(length - 1);
				channel.write(ByteBuffer.wrap(new byte[] { 0 }));
			}
		} catch (IOException e) {
			throw new IOException("create spares file fail,path:" + filePath + " length:" + length, e);
		}
	}

	/**
	 * 使用RandomAccessFile创建指定大小的File
	 */
	public static void createFileWithDefault(String filePath, long length) throws IOException {
		Path path = Paths.get(filePath);
		try {
			Files.deleteIfExists(path);
			try (RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "rw");) {
				randomAccessFile.setLength(length);
			}
		} catch (IOException e) {
			throw new IOException("create spares file fail,path:" + filePath + " length:" + length, e);
		}
	}

	public static String getSystemFileType(String filePath) throws IOException {
		File file = new File(filePath);
		if (!file.exists()) {
			file = file.getParentFile();
		}
		return Files.getFileStore(file.toPath()).type();
	}

	/**
	 * 读取文件，可以读取一个具体的文件，也可以读取一个目录下所有文件
	 *
	 * @param filePathName
	 *            文件名或目录名
	 * @throws IOException
	 */
	public static List<String> readFile(String filePathName) throws IOException {
		File file = new File(filePathName);
		// 是一个具体的文件
		if (file.isFile()) {
			return loadFile(filePathName);
		}
		// 是一个目录
		else if (file.isDirectory()) {
			File[] files = file.listFiles();
			for (File pfile : files) {
				if (pfile.isFile()) {
					try {
						return loadFile(pfile.getAbsolutePath());
					} catch (Exception e) {
						throw new RuntimeException("读取目录文件发生异常。", e);
					}
				}
			}
		}
		return null;
	}


	/**
	 * 把文件载入到链表中
	 *
	 * @param filePathName
	 * @throws IOException
	 */
	public static List<String> loadFile(String filePathName) throws IOException {

		List<String> arrayList = new ArrayList<>();
		InputStreamReader isr = new InputStreamReader(new FileInputStream(filePathName), "utf-8");
		BufferedReader br = new BufferedReader(isr);
		String str = br.readLine();
		while (str != null) {
			str = br.readLine();
			arrayList.add(str);
		}
		br.close();
		isr.close();
		return arrayList;
	}

	public static byte[] getBytes(File file){
		byte[] buffer = null;
		try {
			FileInputStream fis = new FileInputStream(file);
			ByteArrayOutputStream bos = new ByteArrayOutputStream(1000);
			byte[] b = new byte[1000];
			int n;
			while ((n = fis.read(b)) != -1) {
				bos.write(b, 0, n);
			}
			fis.close();
			bos.close();
			buffer = bos.toByteArray();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return buffer;
	}


	public  static void pdf2Html(byte[] body, HttpServletResponse response) throws Throwable {
		response.setContentType("application/pdf");
		OutputStream out = response.getOutputStream();
		out.write(body);
		out.flush();
		out.close();
	}

	// 判断文件夹是否存在
	public static void judeDirExists(File file ) {
		if (!file.exists()) {
			file.mkdirs();
		}
	}
	
	// 判断文件夹是否存在
	public static void judeDirExists(String dirPath) {
		File file = new File(dirPath);
		if (!file.exists()) {
			file.mkdirs();
		}
	}
	
	/**
     * 获取路径下的所有文件/文件夹
     * @param directoryPath 需要遍历的文件夹路径
     * @param isAddDirectory 是否将子文件夹的路径也添加到list集合中
     * @return
     */
    public static List<String> getAllFile(String directoryPath,boolean isAddDirectory) {
        List<String> list = new ArrayList<String>();
        File baseFile = new File(directoryPath);
        if (baseFile.isFile() || !baseFile.exists()) {
            return list;
        }
        File[] files = baseFile.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                if(isAddDirectory){
                    list.add(file.getAbsolutePath());
                }
                list.addAll(getAllFile(file.getAbsolutePath(),isAddDirectory));
            } else {
                list.add(file.getAbsolutePath());
            }
        }
        return list;
    }
	
    /**
     * 获取文件夹路径
     * @param filePath
     * @return
     */
    public static String getDirectory(String filePath){
    	File file = new File(filePath);
    	if(file.isDirectory()){
    		return filePath;
    	}
    	else if(file.isFile()){
    		return filePath.replace(file.getName(), "");
    	}
    	return null;
    }

	/**
	 * 获取文件扩展名
	 * @param filename
	 * @return
	 */
	public static String getExtend(String filename) {
		return getExtend(filename, "");
	}

	/**
	 * 获取文件扩展名
	 * 
	 * @param filename
	 * @return
	 */
	public static String getExtend(String filename, String defExt) {
		if ((filename != null) && (filename.length() > 0)) {
			int i = filename.lastIndexOf('.');

			if ((i > 0) && (i < (filename.length() - 1))) {
				return (filename.substring(i+1)).toLowerCase();
			}
		}
		return defExt.toLowerCase();
	}

	/**
	 * 获取文件名称[不含后缀名]
	 * 
	 * @param
	 * @return String
	 */
	public static String getFilePrefix(String fileName) {
		int splitIndex = fileName.lastIndexOf(".");
		return fileName.substring(0, splitIndex).replaceAll("\\s*", "");
	}

	/**
	 * 删除指定的文件
	 * 
	 * @param strFileName
	 *            指定绝对路径的文件名
	 * @return 如果删除成功true否则false
	 */
	public static boolean delete(String strFileName) {
		File fileDelete = new File(strFileName);
		if (!fileDelete.exists() || !fileDelete.isFile()) {
			return false;
		}
		return fileDelete.delete();
	}

	/**
	 * 将 file 转化为 Base64 
	 * @param path
	 * @return
	 */
	public static String fileToBase64(String path) {
		File file = new File(path);
		FileInputStream inputFile;
		try {
			inputFile = new FileInputStream(file);
			byte[] buffer = new byte[(int) file.length()];
			inputFile.read(buffer);
			inputFile.close();
			return Base64.getEncoder().encodeToString(buffer);
		} catch (Exception e) {
			throw new RuntimeException("文件路径无效\n" + e.getMessage());
		}
	}
	
	/**
	 * 将Base64 转换为file文件 
	 * @param base64
	 * @param path
	 * @return
	 */
	public static boolean base64ToFile(String base64, String path) {
		byte[] buffer;
		try {
			buffer = Base64.getDecoder().decode(base64);
			File file = new File(path);  
			FileOutputStream out = new FileOutputStream(file);
			out.write(buffer);
			out.flush();
			out.close();
			return true;
		} catch (Exception e) {
			throw new RuntimeException("base64字符串异常或地址异常\n" + e.getMessage());
		}
	}
	
	/** 
     * 生成数据文件 
     *  
     * @param filePath 写入文件的路径 
     * @param base64 写入的字符串内容 
     * @return 
     */  
	public static boolean string2File(String base64, String filePath){
	    BufferedReader bufferedReader = null;  
	    BufferedWriter bufferedWriter;  
		boolean flag = true;  
        try {  
            File file = new File(filePath);  
            if (!file.exists()) {  
                file.createNewFile();  
            }  
            bufferedReader = new BufferedReader(new StringReader(base64));  
            bufferedWriter = new BufferedWriter(new FileWriter(file));  
            char buffer[] = new char[1024];  
            int len;  
            while ((len = bufferedReader.read(buffer)) != -1) {  
                bufferedWriter.write(buffer, 0, len);  
            }  
            bufferedWriter.flush();  
            bufferedReader.close();  
            bufferedWriter.close();  
        } catch (IOException e) {
        	e.printStackTrace();
            flag = false;
            return flag;  
        } finally {  
            if (bufferedReader != null) {  
                try {  
                    bufferedReader.close();  
                } catch (IOException e) {  
                	logger.error(e.getMessage());
                }  
            }  
        }  
        return flag;  
    } 
	
	/** 
     * 读取数据文件 
     * @param filePath 读取的文件路径 
     * @param encoding 读取后的字符串编码集设置 
     * @return 
     */  
	public static String file2String(String filePath, String encoding) { 
		InputStreamReader reader = null;  
        StringWriter writer = new StringWriter();  
        File file = new File(filePath);  
        try {  
            if (encoding == null || "".equals(encoding.trim())) {  
                reader = new InputStreamReader(new FileInputStream(file));  
            } else {  
                reader = new InputStreamReader(new FileInputStream(file), encoding);  
            }  
            // 将输入流写入输出流  
            char[] buffer = new char[1024];  
            int n = 0;  
            while (-1 != (n = reader.read(buffer))) {  
                writer.write(buffer, 0, n);  
            }  
        } catch (Exception e) {
			e.printStackTrace();
        	logger.error(e.getMessage());
            return null;  
        } finally {  
            if (reader != null)  
                try {  
                    reader.close();  
                } catch (IOException e) {  
                	logger.error(e.getMessage()); 
                }  
        }  
        // 返回转换结果  
        if (writer != null) {  
            return writer.toString();  
        } else {  
            return null;  
        }  
    }
	
}
